/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2010 huliqing, huliqing.cn@gmail.com
 *
 * This file is part of QFaces.
 * QFaces is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * QFaces is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with QFaces.  If not, see <http://www.gnu.org/licenses/>.
 *
 * 这个文件是QFaces的一部分。
 * 您可以单独使用或分发这个文件，但请不要移除这个头部声明信息.
 * QFaces是一个自由软件，您可以自由分发、修改其中的源代码或者重新发布它，
 * 新的任何修改后的重新发布版必须同样在遵守LGPL3或更后续的版本协议下发布.
 * 关于LGPL协议的细则请参考COPYING、COPYING.LESSER文件，
 * 您可以在QFaces的相关目录中获得LGPL协议的副本，
 * 如果没有找到，请连接到 http://www.gnu.org/licenses/ 查看并获取副本。
 *
 * - Author: Huliqing
 * - Contact: huliqing.cn@gmail.com
 * - License: GNU Lesser General Public License (LGPL)
 * - Blog and source code availability: http://www.huliqing.name/
 */

package name.huliqing.qfaces.component;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import javax.el.MethodExpression;
import javax.faces.component.UIComponent;
import javax.faces.component.UIComponentBase;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.xml.parsers.ParserConfigurationException;
import name.huliqing.qfaces.QFaces;
import name.huliqing.qfaces.XmlOper;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
 
/**
 *
 * @author huliqing
 */
public class InputHelp extends UIComponentBase implements Ajax {
	private final static Logger logger = Logger.getLogger(InputHelp.class.getName());
	
	// ---- 属性参数
	
	// 侦听器(ok)
    private MethodExpression listener;
    
    // 组件所服务的目标组件ID（完整的客户端ID，可能非JSF组件）(ok)
    private String _for;
    
    // Title，用半角逗号“，”分隔各列，如：“姓名,年龄,电话”
    private String title;
    
    // Header
    private String header;
    
    // Footer
    private String footer;
    
    //  组件默认提供一个引用值(for参数所指定的目标组件的值)给listener所绑定的方法参数,可以从listener的方法参数中获得这个值,如: args[0].
    //	当你需要引用页面中的多个组件的值时,你可以使用ref这个参数,如: ref="id1,id2"
    //	这样可以从后台的args参数中依次获得页面组件id2,id3的值,像这样(第一个值为for参数所指定的组件值):
    //	String id1Value = args[0];
    //	String id2Value = args[1];
    //	String id3Value = args[2];
    private String ref;
    
    // 绑定到js fun, 当用mouse选中(click)某一行之后要执行的动作
    private String onSelect;
    
    // 当执行选择时，以那一列作为值赋给目标组件,默认为0,即首列，每次以首列作为赋值条件，赋给目标组件.
    // 如：输出结果为三列，e.g. "name", "age", "email", selectIndex=1, 则赋值操作将以 "age"列的值来进行
    // 如果该值大于可用的最后列，则以最后一列为准，如上，如果 selectIndex=3, 则以最后一列（"email")来作赋值操作
    private Integer selectIndex;
    
    // 检测频率，这个值定义每次执行检测(即检测是否需要进行Ajax请求)的频率。
    // 以毫秒为单位，默认为200. 一般不用改变这个频率
    // 值越小则频率越高，但并不意味着ajax请求频率越高，
    // ajax的请求频率同时受输入框的输入频率和delay的频率影响,即输入频率越高且delay的频率越高(值越小),则Ajax请求频率越高
    private Integer delay;
    
    // 是否自动关闭，该参数定义:是否在点击（鼠标）选择某一个值之后关闭数据面板
    private Boolean autoClose;
    
    // Data panel 的样式
    private String styleClass;			// dataPanel面板样式(ok)
    private String styleSelectedClass;	// 当前选中行的样式(ok)
    private String rowClasses;			// 行样式，格式用半角逗号分隔，如："class1,class2,class3..."(ok)
    private String columnClasses;		// 列样式，格式同上(ok)
    private String headerClass;
    private String footerClass;
    private String titleClass;
    
    // ---- 内部属性
    
    private String ID_CLIENT;	// 组件客户端ID
    private String ID_PANEL;	// 主数据面板的样式类
    
    // ------------------------
 
    public void setListener(MethodExpression listener) {
        this.listener = listener;
    }
    
    @Override
    public String getFamily() {
        return getClass().getName();
    }

    @Override
    public Object saveState(FacesContext fc) {
        Object[] objs = new Object[2];
        objs[0] = super.saveState(fc);
        objs[1] = this.listener;
        return objs;
    }

    @Override
    public void restoreState(FacesContext fc, Object state) {
        Object[] objs = (Object[]) state;
        super.restoreState(fc, objs[0]);
        this.listener = (MethodExpression) objs[1];
    }
    
    // ------------------------ encode

    @Override
    public void encodeBegin(FacesContext fc) throws IOException {
    	QFaces.getResLoader2().loadJavaScript(this, "InputHelp.js");
    	
    	this._for = (String) QFaces.getAttribute(this, "for", "");
    	this.title = (String) QFaces.getAttribute(this, "title", "");
    	this.header = (String) QFaces.getAttribute(this, "header", "");
        this.footer = (String) QFaces.getAttribute(this, "footer", "");
        this.ref = (String) QFaces.getAttribute(this, "ref", "");
        this.styleClass = (String) QFaces.getAttribute(this, "styleClass", "_qf_03_panel");
        this.styleSelectedClass = (String) QFaces.getAttribute(this, "styleSelectedClass", "_qf_03_selected");
        this.rowClasses = (String) QFaces.getAttribute(this, "rowClasses", "");
        this.columnClasses = (String) QFaces.getAttribute(this, "columnClasses", "");
        this.headerClass = (String) QFaces.getAttribute(this, "headerClass", "_qf_03_header");
        this.footerClass = (String) QFaces.getAttribute(this, "footerClass", "_qf_03_footer");
        this.titleClass = (String) QFaces.getAttribute(this, "titleClass", "_qf_03_title");
        this.onSelect = (String) QFaces.getAttribute(this, "onSelect", "");
        this.selectIndex = QFaces.convertToInteger(QFaces.getAttribute(this, "selectIndex", 0));
        this.delay = QFaces.convertToInteger(QFaces.getAttribute(this, "delay", 200));
        this.autoClose = QFaces.convertToBoolean(QFaces.getAttribute(this, "autoClose", Boolean.TRUE));
        
        this.ID_CLIENT = getClientId(fc);
        this.ID_PANEL = ID_CLIENT + ":panel";
        
        // 将for属性指定的ID转换为客户端长ID
        UIComponent target = this.findComponent(_for);
        if (target != null)
        	this._for = target.getClientId(fc);
        
        // 处理客户端长ID, 将组件所引用的ID（short id），转化为客户端的长ID类型，以便客户端
		// 的Javascript可引用
		if (!"".equals(this.ref)) {
			String[] refIds = null;
			if (this.ref.indexOf(",") != -1) {
				refIds = this.ref.split(",");
			} else {
				refIds = new String[]{this.ref};
			}
			StringBuilder tempRef = new StringBuilder("");
			for (String refId : refIds) {
				UIComponent refCom = this.findComponent(refId.trim());
				if (refCom != null) {
					tempRef.append(refCom.getClientId(fc)).append(",");	// 引用了JSF组件
				} else {
					tempRef.append(refId.trim()).append(","); // 可能引用的是一般的Html组件
				}
			}
			this.ref = tempRef.toString();
		}
		if (this.ref.endsWith(",")) this.ref = this.ref.substring(0, this.ref.length() - 1);
    	
		// 开始渲染完整的组件
        ResponseWriter rw = fc.getResponseWriter();
        rw.startElement("div", this);
        rw.writeAttribute("id", this.ID_CLIENT, null);
        rw.writeAttribute("style", "border:0;padding:0;", null);
    }

    @Override
    public void encodeEnd(FacesContext fc) throws IOException {
        ResponseWriter rw = fc.getResponseWriter();
        	rw.startElement("br", this);
        	rw.endElement("br");
	        rw.startElement("div", this);
	        rw.writeAttribute("style", "position:absolute;border:0;padding:0;", null);
	        rw.writeAttribute("id", this.ID_PANEL, null);		        
	        rw.endElement("div");
        rw.endElement("div"); // End render.

        // 初始化
        StringBuilder jsInit = new StringBuilder("Q.F.UIInputHelp.init(")
		        .append(" \"" + this.ID_CLIENT + "\"")
		        .append(",\"" + this.ID_PANEL + "\"")
		        .append(",\"" + (this.listener != null ? this.listener.getExpressionString() : "") + "\"")
		        .append(",\"" + this.getClass().getName() + "\"")
		        .append(",\"" + this._for + "\"")
		        .append(",\"" + this.ref + "\"")
		        .append(",\"" + this.title + "\"")
		        .append(",\"" + this.header + "\"")
		        .append(",\"" + this.footer + "\"")
		        .append(",\"" + this.styleClass + "\"")
		        .append(",\"" + this.styleSelectedClass + "\"")
		        .append(",\"" + this.rowClasses + "\"")
		        .append(",\"" + this.columnClasses + "\"")
		        .append(",\"" + this.headerClass + "\"")
		        .append(",\"" + this.footerClass + "\"")
		        .append(",\"" + this.titleClass + "\"")
		        .append(",\"" + this.onSelect + "\"")
		        .append(",\"" + this.selectIndex + "\"")
		        .append(",\"" + this.delay + "\"")
		        .append(",\"" + this.autoClose.toString() + "\"")
		        .append(")");
        
        QFaces.getResLoader2().loadJavaScriptInit(this, jsInit.toString());
    }
    

	public void ajax(Map<String, String> params, FacesContext fc) {
        String listenerExp = params.get("listenerExp");
        String targetValue = params.get("targetValue"); // 获取目标组件的值
        Integer refCount = Integer.valueOf(params.get("refCount"));
        MethodExpression _listener = QFaces.createMethodExpression(listenerExp, List.class, new Class[]{String[].class});
        
        // 数组第一个值为目标组件的输入值
        String[] args = new String[refCount + 1];
        args[0] = targetValue;
        for (int i = 0; i < refCount; i++) {
            String refValue = params.get("refValue" + i);
            args[i + 1] = refValue;
        }
        List<Object> results = (List<Object>) _listener.invoke(fc.getELContext(), new Object[]{args});
        this.response(fc, results);
    }
    
    /**
     * format of return values
     * <root>
     *      <row>
     *          <column value="" />
     *          <column value="" />
     *      </row>
     *      <row>
     *          <column value="" />
     *          <column value="" />
     *      </row>
     * </root>
     * @param fc
     * @param results
     */
	private void response(FacesContext fc, List<Object> results) {
        try {
            Document doc = XmlOper.newDocument();
            Element nodeRoot = doc.createElement("root");
            doc.appendChild(nodeRoot);
            if (results != null && !results.isEmpty()) {
            	for (Object temp : results) {
            		if (temp instanceof List) {
            			List<String> values = (List<String>) temp;
            			Element rowElement = doc.createElement("row");
            			nodeRoot.appendChild(rowElement);
            			for (String value : values) {
            				Element columnElement = doc.createElement("column");
            				columnElement.setAttribute("value", value);
            				rowElement.appendChild(columnElement);
            			}
            		} else if (temp instanceof String) {
            			Element rowElement = doc.createElement("row");
            			Element columnElement = doc.createElement("column");
            			columnElement.setAttribute("value", (String) temp);
            			nodeRoot.appendChild(rowElement);
            			rowElement.appendChild(columnElement);
            		}
            	}
            }
            QFaces.getResponse().responseXML(fc, doc);
        } catch (ParserConfigurationException e) {
        	logger.warning(e.getMessage());
        }
    }
}
